﻿namespace ImageProcessing
{
    using System;
    using System.Collections.Generic;
    using System.Drawing;
    using System.Drawing.Imaging;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;

    struct bitmaxP
    {
        public int bitIndex, x, y;
    };

    /// <summary>
    /// Type of the Noise
    /// </summary>
    public enum NoiseType
    {
        /// <summary>
        /// Slat and Pepper Noise
        /// </summary>
        Salt_pepper,

        /// <summary>
        /// Uniform Noise
        /// </summary>
        Uniform,

        /// <summary>
        /// Gaussian Noise
        /// </summary>
        Gaussian
    }

    /// <summary>
    /// Class the carries noise creation functions
    /// </summary>
    public class Noise
    {
        /// <summary>
        /// Max RNG value
        /// </summary>
        private int maxValue;

        /// <summary>
        /// Points array
        /// </summary>
        private Point[] pointsArray;

        /// <summary>
        /// R N G
        /// </summary>
        private Random randGenerator;

        /// <summary>
        /// Initializes a new instance of the <see cref="Noise"/> class.
        /// </summary>
        /// <param name="height">The height.</param>
        /// <param name="width">The width.</param>
        public Noise(int height, int width)
        {
            this.pointsArray = new Point[height * width];
            this.maxValue = height * width;

            this.randGenerator = new Random();

            int index = 0;
            for (int i = 0; i < height; i++)
            {
                for (int j = 0; j < width; j++)
                {
                    this.pointsArray[index].X = j;
                    this.pointsArray[index].Y = i;
                    index++;
                }
            }
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Noise"/> class.
        /// </summary>
        public Noise()
        {
        }

        /// <summary>
        /// Gets specfic bit from a byte.
        /// </summary>
        /// <param name="b">The b.</param>
        /// <param name="bitNumber">The bit number.</param>
        /// <returns>int (0 or 1) represent the value of the bit</returns>
        public static int GetBit(byte b, int bitNumber)
        {
            // if ((b & (1 << bitNumber)) != 0)
            // {
            //  return 1;
            // }
            // else
            // {
            //  return 0;
            // }
            string s = Convert.ToString(b, 2);
            while (s.Length < 8)
            {
                s = '0' + s;
            }

            return s[bitNumber];
        }

        /// <summary>
        /// Convert Binary To Decimal.
        /// </summary>
        /// <param name="binaryNumber">The binary number.</param>
        /// <returns> Integer number in Decimal</returns>
        public static int ToDecimal(string binaryNumber)
        {
            long l = Convert.ToInt64(binaryNumber, 2);
            int i = (int)l;
            return i;
        }

        /// <summary>
        /// Gets the fililter values.
        /// </summary>
        /// <param name="mask">The mask.</param>
        /// <param name="type">The type.</param>
        /// <returns>min max midpoint median</returns>
        public static int GetFililterValues(int[] mask, FilterType type)
        {
            int min, max, midPoint, medianKey;
            switch (type)
            {
                case FilterType.Min2D:
                    Noise.FindMinMax(mask, out min, out max);
                    return min;
                case FilterType.Max2D:
                    Noise.FindMinMax(mask, out min, out max);
                    return max;
                case FilterType.MidPoint:
                    Noise.FindMinMax(mask, out min, out max);
                    midPoint = (min + max) / 2;
                    return midPoint;
                case FilterType.Median:
                    medianKey = Noise.RandomizedSelect(mask, 0, mask.Length - 1, mask.Length / 2);
                    return medianKey;
                default:
                    return -1;
            }
        }

        /// <summary>
        /// Gets the fililter values.
        /// </summary>
        /// <param name="mask">The mask.</param>
        /// <param name="min">The min.</param>
        /// <param name="max">The max.</param>
        /// <param name="medianKey">The median key.</param>
        public static void GetMinMaxMed(int[] mask, ref int min, ref int max, ref int medianKey)
        {
            Noise.FindMinMax(mask, out min, out max);
            Noise.FindMinMax(mask, out min, out max);
            Noise.FindMinMax(mask, out min, out max);
            medianKey = Noise.RandomizedSelect(mask, 0, mask.Length - 1, mask.Length / 2);
        }

        /// <summary>
        /// Partitions the specified arr.
        /// </summary>
        /// <param name="arr">The arr.</param>
        /// <param name="p">The p.</param>
        /// <param name="r">The r.</param>
        /// <returns> partition fn</returns>
        public static int Partition(int[] arr, int p, int r)
        {
            int pivot = arr[p];
            int i = p;
            int j = r + 1;
            int temp = 0;
            while (true)
            {
                do
                {
                    j--;
                }
                while ((arr[j] > pivot) && j != p);

                do
                {
                    i++;
                }
                while ((arr[i] < pivot) && i != r);

                if (i < j)
                {
                    temp = arr[i];
                    arr[i] = arr[j];
                    arr[j] = temp;
                }
                else
                {
                    temp = arr[j];
                    arr[j] = arr[p];
                    arr[p] = temp;
                    return j;
                }
            }
        }

        /// <summary>
        /// Randomizeds the select.
        /// </summary>
        /// <param name="array">The A.</param>
        /// <param name="p">The p.</param>
        /// <param name="r">The r.</param>
        /// <param name="i">The i.</param>
        /// <returns>A random value</returns>
        public static int RandomizedSelect(int[] array, int p, int r, int i)
        {
            // a:array ,p:start   r:end  i:meadian
            int q, k;
            if (p == r)
            {
                return array[p];
            }

            q = Noise.Partition(array, p, r);
            k = q - p + 1;
            if (i == k)
            {
                return array[q];
            }
            else if (i < k)
            {
                return RandomizedSelect(array, p, q - 1, i);
            }
            else
            {
                return RandomizedSelect(array, q + 1, r, i - k);
            }
        }

        /// <summary>
        /// Finds the min max.
        /// </summary>
        /// <param name="arr">The arr.</param>
        /// <param name="min">The min.</param>
        /// <param name="max">The max.</param>
        public static void FindMinMax(int[] arr, out int min, out int max)
        {
            // assum length of array is odd in this case
            min = max = arr[0];
            for (int i = 2; i < arr.Length; i = i + 2)
            {
                if (arr[i] > arr[i - 1])
                {
                    if (arr[i] > max)
                    {
                        max = arr[i];
                    }

                    if (arr[i - 1] < min)
                    {
                        min = arr[i - 1];
                    }
                }
                else
                {
                    if (arr[i - 1] > max)
                    {
                        max = arr[i - 1];
                    }

                    if (arr[i] < min)
                    {
                        min = arr[i];
                    }
                }
            }
        }

        /// <summary>
        /// Gets the point filter.
        /// </summary>
        /// <param name="mask">The mask.</param>
        /// <param name="type">The type.</param>
        /// <returns>point of spicfic filter</returns>
        public static Point GetPointFilter(int[,] mask, FilterType type)
        {
            var temp = new Dictionary<Point, int>();
            int heigth = mask.GetLength(0);
            int width = mask.GetLength(1);
            for (int i = 0; i < heigth; i++)
            {
                for (int j = 0; j < width; j++)
                {
                    temp.Add(new Point(i, j), mask[i, j]);
                }
            }

            // 2.
            // Order by values.
            // Use LINQ to specify sorting by value.
            var items = from k in temp.Keys
                        orderby temp[k] ascending
                        select k;

            int maskSize = heigth * width;
            int count = 1;
            Point minKey = new Point(), maxKey = new Point(), midKey = new Point(), medianKey = new Point();
            int minval = 0, maxVal = 0, midVal = 0, midianVal = 0;

            foreach (Point p in items)
            {
                if (count == 1)
                {
                    minval = temp[p];
                    minKey = p;
                }
                else if (count == maskSize)
                {
                    maxVal = temp[p];
                    maxKey = p;
                }
                else if (count == (maskSize + 1) / 2)
                {
                    medianKey = p;
                    midianVal = temp[p];
                }

                count++;
            }

            midVal = (maxVal + midVal) / 2;
            switch (type)
            {
                case FilterType.Min2D:
                    return minKey;
                case FilterType.Max2D:
                    return maxKey;
                case FilterType.MidPoint:
                    return midKey;
                case FilterType.Median:
                    return medianKey;
                default:
                    return new Point(-1, -1);
            }
        }

        /// <summary>
        /// Gets the mid point filter.
        /// </summary>
        /// <param name="mask">The mask.</param>
        /// <param name="pmax">The pmax.</param>
        /// <param name="pmin">The pmin.</param>
        public static void GetMidPointFilter(int[,] mask, ref Point pmax, ref Point pmin)
        {
            var temp = new Dictionary<Point, int>();
            int heigth = mask.GetLength(0);
            int width = mask.GetLength(1);
            for (int i = 0; i < heigth; i++)
            {
                for (int j = 0; j < width; j++)
                {
                    temp.Add(new Point(i, j), mask[i, j]);
                }
            }

            // 2.
            // Order by values.
            // Use LINQ to specify sorting by value.
            var items = from k in temp.Keys
                        orderby temp[k] ascending
                        select k;

            int maskSize = heigth * width;
            int count = 1;
            Point minKey = new Point(), maxKey = new Point(), medianKey = new Point();
            int minval = 0, maxVal = 0, midianVal = 0;

            foreach (Point p in items)
            {
                if (count == 1)
                {
                    minval = temp[p];
                    minKey = p;
                }
                else if (count == maskSize)
                {
                    maxVal = temp[p];
                    maxKey = p;
                }
                else if (count == (maskSize + 1) / 2)
                {
                    medianKey = p;
                    midianVal = temp[p];
                }

                count++;
            }

            pmax = maxKey;
            pmin = minKey;
        }

        /// <summary>
        /// Adds the salt and pepper.
        /// </summary>
        /// <param name="image">The image.</param>
        /// <param name="saltProbability">The salt probability.</param>
        /// <param name="pepperProbability">The pepper probability.</param>
        /// <param name="noisePercentage">The noise percentage.</param>
        /// <returns>Image with noise</returns>
        public static Bitmap AddSaltAndPepper(Bitmap image, double saltProbability, double pepperProbability, int noisePercentage)
        {
            int height = image.Height;
            int width = image.Width;

            Bitmap bmp = image;
            BitmapData bmpd = bmp.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
            int extraBytes = bmpd.Stride - (width * 3);
            unsafe
            {
                int i, j, pixelIndex = 0;
                byte* pOriginal = (byte*)bmpd.Scan0;
                int noisePixels = height * width * noisePercentage;
                noisePixels /= 100;
                int saltNumber = (int)Math.Floor(noisePixels * saltProbability);
                int pepperNumber = (int)Math.Floor(noisePixels * pepperProbability);

                Noise noiseInstance = new Noise(height, width);
                Point newPoint;
                for (i = 0; i < saltNumber; i++)
                {
                    newPoint = noiseInstance.GetRandomPoint();
                    pixelIndex = (newPoint.Y * width * 3) + (newPoint.Y * extraBytes) + (newPoint.X * 3);

                    pOriginal[pixelIndex] = 255;
                    pOriginal[pixelIndex + 1] = 255;
                    pOriginal[pixelIndex + 2] = 255;
                }

                for (j = 0; j < pepperNumber; j++)
                {
                    newPoint = noiseInstance.GetRandomPoint();
                    pixelIndex = (newPoint.Y * width * 3) + (newPoint.Y * extraBytes) + (newPoint.X * 3);

                    pOriginal[pixelIndex] = 0;
                    pOriginal[pixelIndex + 1] = 0;
                    pOriginal[pixelIndex + 2] = 0;
                }
            }

            bmp.UnlockBits(bmpd);
            return bmp;
        }

        /// <summary>
        /// Adds the periodic noise.
        /// </summary>
        /// <param name="image">The image.</param>
        /// <param name="amplitude">The amplitude.</param>
        /// <param name="fX">The f X.</param>
        /// <param name="fY">The f Y.</param>
        /// <param name="pX">The p X.</param>
        /// <param name="pY">The p Y.</param>
        /// <returns>Image with periodic noise</returns>
        public static ImageStructure[,] AddPeriodicNoise(ImageStructure[,] image, byte amplitude, double fX, double fY, double pX, double pY)
        {
            int height = image.GetLength(0);
            int width = image.GetLength(1);
            ImageStructure[,] newImage = new ImageStructure[height, width];
            ImageStructure[,] sinImage = new ImageStructure[height, width];
            double sinWave;
            double[,] norImageR = new double[height, width];
            double[,] norImageG = new double[height, width];
            double[,] norImageB = new double[height, width];
            for (int i = 0; i < height; i++)
            {
                for (int j = 0; j < width; j++)
                {
                    sinWave = amplitude * Math.Sin(((2.0 * Math.PI * j * fX) / width) + pX + ((2.0 * Math.PI * j * fY) / height) + pY);

                    // SinImage[i, j].R = (byte)(amplitude * Math.Sin(((2 * Math.PI * j * fX) / width) + pX + ((2 * Math.PI * j * fY) / height) + pY));
                    // SinImage[i, j].G = (byte)(amplitude * Math.Sin(((2 * Math.PI * j * fX) / width) + pX + ((2 * Math.PI * j * fY) / height) + pY));
                    // SinImage[i, j].B = (byte)(amplitude * Math.Sin(((2 * Math.PI * j * fX) / width) + pX + ((2 * Math.PI * j * fY) / height) + pY));
                    norImageR[i, j] = sinWave + image[i, j].R;
                    norImageG[i, j] = sinWave + image[i, j].G;
                    norImageB[i, j] = sinWave + image[i, j].B;
                }
            }

            newImage = PixelOperation.Normalization(newImage, height, width, norImageR, norImageG, norImageB);
            return newImage;
        }

        /// <summary>
        /// Adds the uniform noise to the image.
        /// </summary>
        /// <param name="image">The image.</param>
        /// <param name="a">First number in the interval</param>
        /// <param name="b">Last number in the interval</param>
        /// <param name="noisePercentage">The noise percentage.</param>
        /// <returns>Uniform Noisy Image</returns>
        public static ImageStructure[,] AddUniformNoise(ImageStructure[,] image, int a, int b, int noisePercentage)
        {
            int height = image.GetLength(0);
            int width = image.GetLength(1);
            int noisePixels = (int)(((float)1 / (float)(b - a)) * width * height * ((float)noisePercentage / (float)100));
            double[,] red = new double[height, width];
            double[,] green = new double[height, width];
            double[,] blue = new double[height, width];
            ImageStructure[,] newimage = new ImageStructure[height, width];
            DoubleImage[,] newDoubleImage = new DoubleImage[height, width];

            FourierOperations.SeparateColors(ref red, ref green, ref blue, image);

            Noise noiseInstance = new Noise(height, width);
            Point generatedPoint;
            for (int value = a; value < b; value++)
            {
                for (int i = 0; i < noisePixels; i++)
                {
                    generatedPoint = noiseInstance.GetRandomPoint();
                    red[generatedPoint.Y, generatedPoint.X] += value;
                    green[generatedPoint.Y, generatedPoint.X] += value;
                    blue[generatedPoint.Y, generatedPoint.X] += value;
                }
            }

            return PixelOperation.Normalization(newimage, height, width, red, green, blue);
        }

        /// <summary>
        /// Adds the exponential noise.
        /// </summary>
        /// <param name="image">The image.</param>
        /// <param name="a">A value.</param>
        /// <param name="noisePercentage">The noise percentage.</param>
        /// <returns>
        /// image With exponential noise
        /// </returns>
        public static ImageStructure[,] AddExponentialNoise(ImageStructure[,] image, double a, int noisePercentage)
        {
            int numNoise = 0;
            int height = image.GetLength(0);
            int width = image.GetLength(1);
            Noise noiseInstance = new Noise(height, width);
            double[,] noiseValR = new double[height, width];
            double[,] noiseValG = new double[height, width];
            double[,] noiseValB = new double[height, width];

            double[,] newValR = new double[height, width];
            double[,] newValG = new double[height, width];
            double[,] newValB = new double[height, width];

            Point generatedPoint;

            for (int g = 0; g <= 255; g++)
            {
                numNoise = (int)((a * Math.Exp(-a * g)) * width * height * ((float)noisePercentage / (float)100.0));

                for (int i = 1; i < numNoise; i++)
                {
                    generatedPoint = noiseInstance.GetRandomPoint();
                    noiseValR[generatedPoint.Y, generatedPoint.X] = g;
                    noiseValG[generatedPoint.Y, generatedPoint.X] = g;
                    noiseValB[generatedPoint.Y, generatedPoint.X] = g;
                }
            }

            for (int i = 0; i < height; i++)
            {
                for (int j = 0; j < width; j++)
                {
                    newValR[i, j] = image[i, j].R + noiseValR[i, j];
                    newValG[i, j] = image[i, j].G + noiseValG[i, j];
                    newValB[i, j] = image[i, j].B + noiseValB[i, j];
                }
            }

            ImageStructure[,] newImage = new ImageStructure[height, width];

            newImage = PixelOperation.Normalization(newImage, height, width, newValR, newValG, newValB);

            return newImage;
        }

        /// <summary>
        /// Adds the gamma noise.
        /// </summary>
        /// <param name="image">The image.</param>
        /// <param name="a">A value.</param>
        /// <param name="b">The b.</param>
        /// <param name="noisePercentage">The noise percentage.</param>
        /// <returns>
        /// image with Gamma Noise
        /// </returns>
        public static ImageStructure[,] AddGammaNoise(ImageStructure[,] image, double a, double b, int noisePercentage)
        {
            int numNoise = 0;
            int height = image.GetLength(0);
            int width = image.GetLength(1);
            Noise noiseInstance = new Noise(height, width);
            double[,] noiseValR = new double[height, width];
            double[,] noiseValG = new double[height, width];
            double[,] noiseValB = new double[height, width];

            double[,] newValR = new double[height, width];
            double[,] newValG = new double[height, width];
            double[,] newValB = new double[height, width];

            Point generatedPoint;

            for (int g = 0; g <= 255; g++)
            {
                numNoise = (int)(((Math.Pow(a, b) * Math.Pow(g, b - 1) / Noise.Fact(b - 1)) * Math.Exp(-a * g)) * width * height * ((float)noisePercentage / (float)100.0));

                for (int i = 1; i < numNoise; i++)
                {
                    generatedPoint = noiseInstance.GetRandomPoint();
                    noiseValR[generatedPoint.Y, generatedPoint.X] = g;
                    noiseValG[generatedPoint.Y, generatedPoint.X] = g;
                    noiseValB[generatedPoint.Y, generatedPoint.X] = g;
                }
            }

            for (int i = 0; i < height; i++)
            {
                for (int j = 0; j < width; j++)
                {
                    newValR[i, j] = image[i, j].R + noiseValR[i, j];
                    newValG[i, j] = image[i, j].G + noiseValG[i, j];
                    newValB[i, j] = image[i, j].B + noiseValB[i, j];
                }
            }

            ImageStructure[,] newImage = new ImageStructure[height, width];

            newImage = PixelOperation.Normalization(newImage, height, width, newValR, newValG, newValB);

            return newImage;
        }

        /// <summary>
        /// Adds the raleigh noise.
        /// </summary>
        /// <param name="image">The image.</param>
        /// <param name="a">A value.</param>
        /// <param name="b">The b.</param>
        /// <param name="noisePercentage">The noise percentage.</param>
        /// <returns>
        /// image with Raleigh Noise
        /// </returns>
        public static ImageStructure[,] AddRaleighNoise(ImageStructure[,] image, double a, double b, int noisePercentage)
        {
            int numNoise = 0;
            int height = image.GetLength(0);
            int width = image.GetLength(1);
            Noise noiseInstance = new Noise(height, width);
            double[,] noiseValR = new double[height, width];
            double[,] noiseValG = new double[height, width];
            double[,] noiseValB = new double[height, width];

            double[,] newValR = new double[height, width];
            double[,] newValG = new double[height, width];
            double[,] newValB = new double[height, width];

            Point generatedPoint;
            double temp = width * height * ((double)noisePercentage / (double)100);
            for (int g = 0; g <= 255; g++)
            {
                numNoise = (int)((((double)2 / (double)b) * (g - a) * Math.Exp(-Math.Pow((g - a), 2) / (double)b)) * temp);

                for (int i = 1; i < numNoise; i++)
                {
                    generatedPoint = noiseInstance.GetRandomPoint();
                    noiseValR[generatedPoint.Y, generatedPoint.X] = g;
                    noiseValG[generatedPoint.Y, generatedPoint.X] = g;
                    noiseValB[generatedPoint.Y, generatedPoint.X] = g;
                }
            }

            for (int i = 0; i < height; i++)
            {
                for (int j = 0; j < width; j++)
                {
                    newValR[i, j] = image[i, j].R + noiseValR[i, j];
                    newValG[i, j] = image[i, j].G + noiseValG[i, j];
                    newValB[i, j] = image[i, j].B + noiseValB[i, j];
                }
            }

            ImageStructure[,] newImage = new ImageStructure[height, width];

            newImage = PixelOperation.Normalization(newImage, height, width, newValR, newValG, newValB);

            return newImage;
        }

        /// <summary>
        /// Adds the gaussian noise.
        /// </summary>
        /// <param name="image">The image.</param>
        /// <param name="sigma">The sigma.</param>
        /// <param name="meyo">The meyo.</param>
        /// <param name="noisePercentage">The noise percentage.</param>
        /// <returns>new image with gaussianNoise</returns>
        public static ImageStructure[,] AddGaussianNoise(ImageStructure[,] image, double sigma, double meyo, int noisePercentage)
        {
            int numNoise = 0;
            int height = image.GetLength(0);
            int width = image.GetLength(1);
            Noise noiseInstance = new Noise(height, width);
            double[,] noiseValR = new double[height, width];
            double[,] noiseValG = new double[height, width];
            double[,] noiseValB = new double[height, width];

            double[,] newValR = new double[height, width];
            double[,] newValG = new double[height, width];
            double[,] newValB = new double[height, width];

            Point generatedPoint;
            double noisePixels = height * width * ((double)noisePercentage / 100);

            for (int g = 0; g <= 255; g++)
            {
                numNoise = Convert.ToInt32((1 / Math.Sqrt(2 * Math.PI * sigma * sigma)) * Math.Exp(-Math.Pow((g - meyo), 2) / (2 * sigma * sigma)) * noisePixels);
                for (int i = 1; i < numNoise; i++)
                {
                    generatedPoint = noiseInstance.GetRandomPoint();
                    noiseValR[generatedPoint.Y, generatedPoint.X] = g;
                    noiseValG[generatedPoint.Y, generatedPoint.X] = g;
                    noiseValB[generatedPoint.Y, generatedPoint.X] = g;
                }
            }

            for (int i = 0; i < height; i++)
            {
                for (int j = 0; j < width; j++)
                {
                    newValR[i, j] = image[i, j].R + noiseValR[i, j];
                    newValG[i, j] = image[i, j].G + noiseValG[i, j];
                    newValB[i, j] = image[i, j].B + noiseValB[i, j];
                }
            }

            ImageStructure[,] newImage = new ImageStructure[height, width];

            newImage = PixelOperation.Normalization(newImage, height, width, newValR, newValG, newValB);

            return newImage;
        }

        /// <summary>
        /// Adds the gaussian noise bitmab.
        /// </summary>
        /// <param name="image">The image.</param>
        /// <param name="sigma">The sigma.</param>
        /// <param name="meyo">The meyo.</param>
        /// <param name="noisePercentage">The noise percentage.</param>
        /// <returns>new image with Gaussian noise</returns>
        public static Bitmap AddGaussianNoiseBitmab(Bitmap image, double sigma, double meyo, int noisePercentage)
        {
            int numNoise = 0;
            int height = image.Width;
            int width = image.Height;
            Noise noiseInstance = new Noise(height, width);
            double[,] noiseValR = new double[height, width];
            double[,] noiseValG = new double[height, width];
            double[,] noiseValB = new double[height, width];

            double[,] newValR = new double[height, width];
            double[,] newValG = new double[height, width];
            double[,] newValB = new double[height, width];

            Point generatedPoint;
            int originalPixelIndex;
            double noisePixels = height * width * ((double)noisePercentage / 100);

            unsafe
            {
                BitmapData bmpNewD = image.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
                int newExtraBytes = bmpNewD.Stride - (width * 3);
                byte* oldP = (byte*)bmpNewD.Scan0;

                for (int g = 0; g <= 255; g++)
                {
                    numNoise = Convert.ToInt32((1 / Math.Sqrt(2 * Math.PI * sigma * sigma)) * Math.Exp(-Math.Pow((g - meyo), 2) / (2 * sigma * sigma)) * noisePixels);
                    for (int i = 1; i < numNoise; i++)
                    {
                        generatedPoint = noiseInstance.GetRandomPoint();
                        noiseValR[generatedPoint.Y, generatedPoint.X] = g;
                        noiseValG[generatedPoint.Y, generatedPoint.X] = g;
                        noiseValB[generatedPoint.Y, generatedPoint.X] = g;
                    }
                }

                for (int i = 0; i < height; i++)
                {
                    for (int j = 0; j < width; j++)
                    {
                        originalPixelIndex = (i * image.Width * 3) + (i * newExtraBytes) + (j * 3);

                        newValR[i, j] = oldP[originalPixelIndex] + noiseValR[i, j];
                        newValG[i, j] = oldP[originalPixelIndex + 1] + noiseValG[i, j];
                        newValB[i, j] = oldP[originalPixelIndex + 2] + noiseValB[i, j];
                    }
                }
                image.UnlockBits(bmpNewD);
            }

            Bitmap newImage = PixelOperation.Normalizationbitmap(height, width, newValR, newValG, newValB);

            return newImage;
        }

        /// <summary>
        /// Gets the color from bit mixing.
        /// </summary>
        /// <param name="colorBitMixed">The color bit mixed.</param>
        /// <returns>Gets color after bitmixing</returns>
        public static ImageStructure GetColorFromBitMixing(int colorBitMixed)
        {
            string s = Convert.ToString(colorBitMixed, 2);
            while (s.Length < 24)
            {
                s = '0' + s;
            }

            ImageStructure color = new ImageStructure();
            color.B = Convert.ToByte(string.Empty + s[0] + s[3] + s[6] + s[9] + s[12] + s[15] + s[18] + s[21], 2);
            color.G = Convert.ToByte(string.Empty + s[1] + s[4] + s[7] + s[10] + s[13] + s[16] + s[19] + s[22], 2);
            color.R = Convert.ToByte(string.Empty + s[2] + s[5] + s[8] + s[11] + s[14] + s[17] + s[20] + s[23], 2);
            return color;
        }

        /// <summary>
        /// Gets the color from bit mixing two.
        /// </summary>
        /// <param name="colorBitMixed">The color bit mixed.</param>
        /// <returns>Color from bitmixed</returns>
        public static ImageStructure GetColorFromBitMixingTwo(int colorBitMixed)
        {
            string s = Convert.ToString(colorBitMixed, 2);
            while (s.Length < 24)
            {
                s = '0' + s;
            }

            ImageStructure color = new ImageStructure();
            color.B = Convert.ToByte(string.Empty + s[0] + s[3] + s[6] + s[9] + s[12] + s[15] + s[18] + s[21], 2);
            color.G = Convert.ToByte(string.Empty + s[1] + s[4] + s[7] + s[10] + s[13] + s[16] + s[19] + s[22], 2);
            color.R = Convert.ToByte(string.Empty + s[2] + s[5] + s[8] + s[11] + s[14] + s[17] + s[20] + s[23], 2);
            return color;
        }

        /// <summary>
        /// Gets the random point.
        /// </summary>
        /// <returns>
        /// Get point
        /// </returns>
        public Point GetRandomPoint()
        {
            Point randPoint = new Point();
            int index = this.randGenerator.Next(this.maxValue);

            randPoint.X = this.pointsArray[index].X;
            randPoint.Y = this.pointsArray[index].Y;

            this.maxValue--;
            this.pointsArray[index] = this.pointsArray[this.maxValue < 0 ? 0 : this.maxValue];

            return randPoint;
        }

        /// <summary>
        /// Linear convolution Fucntion.
        /// </summary>
        /// <param name="image">The image.</param>
        /// <param name="k">The k.</param>
        /// <param name="fT">The f T.</param>
        /// <param name="aT">A T.</param>
        /// <param name="pP">The p P.</param>
        /// <returns>
        /// Image after appling the mask on it
        /// </returns>
        public Bitmap LinearConvolution(Bitmap image, int k, FilterType fT, AddingType aT, PostProcessing pP)
        {
            int height = image.Height;
            int width = image.Width;
            Bitmap bmp = image;
            Bitmap bmpNew = new Bitmap(width, height, PixelFormat.Format24bppRgb);
            unsafe
            {
                BitmapData bmpD = bmp.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
                BitmapData bmpNewD = bmpNew.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
                int oldExtraBytes = bmpD.Stride - (width * 3);
                int newExtraBytes = bmpNewD.Stride - (width * 3);
                switch (fT)
                {
                    case FilterType.Min2D:
                        PHFastCpp.PHMin2D((int)bmpD.Scan0, (int)bmpNewD.Scan0, width, height, oldExtraBytes, newExtraBytes, k);
                        break;
                    case FilterType.Min1D:
                        //PHFastCpp.PHMin1D((int)bmpD.Scan0, (int)bmpNewD.Scan0, width, height, oldExtraBytes, newExtraBytes, k);
                        int i, j, l, m, pixelIndex, copyFromIndex, bitIndex, bitmixed = 0, b;
                        bitmaxP zmin;
                        int halfWindow = (int)Math.Floor(k / 2.0);

                        int[] bitMixed = new int[width * height];
                        int[] bitMixed1D = new int[width * height];

                        int[] andArr = new int[8] { 128, 64, 32, 16, 8, 4, 2, 1 };

                        byte* p = (byte*)bmpD.Scan0;
                        byte* pNew = (byte*)bmpNewD.Scan0;

                        int xIndex, yIndex;
                        {
                            for (i = 0; i < height; i++)
                            {
                                int shift = 16;
                                for (j = 0; j < width; j++)
                                {
                                    pixelIndex = (i * width * 3) + (i * oldExtraBytes) + (j * 3);
                                    bitIndex = (i * width) + j;
                                    for (b = 0; b < 8; b++)
                                    {
                                        bitmixed |= ((p[pixelIndex + 2] & andArr[b]) << shift) | ((p[pixelIndex + 1] & andArr[b]) << (shift - 1)) | ((p[pixelIndex] & andArr[b]) << (shift - 2));
                                        shift -= 2;
                                    }

                                    bitMixed[bitIndex] = bitmixed;
                                    bitmixed = 0;
                                    shift = 16;
                                }
                            }

                            for (i = 0; i < height; i++)
                            {
                                for (j = 0; j < width; j++)
                                {
                                    bitIndex = (i * width) + j;

                                    zmin.bitIndex = bitIndex;
                                    zmin.x = j;
                                    zmin.y = i;

                                    yIndex = i;
                                    for (m = 0; m <= k; m++)
                                    {
                                        xIndex = j + m - halfWindow;

                                        if (xIndex < 0 || xIndex >= width)
                                            continue;

                                        bitIndex = (yIndex * width) + xIndex;

                                        if (bitMixed[zmin.bitIndex] > bitMixed[bitIndex])
                                        {
                                            zmin.bitIndex = bitIndex;
                                            zmin.x = xIndex;
                                            zmin.y = yIndex;
                                        }
                                    }

                                    copyFromIndex = (zmin.y * width) + zmin.x;
                                    pixelIndex = (i * width) + j;
                                    bitMixed1D[pixelIndex] = bitMixed[copyFromIndex];
                                }
                            }

                            for (i = 0; i < height; i++)
                            {
                                for (j = 0; j < width; j++)
                                {
                                    bitIndex = (i * width) + j;

                                    zmin.bitIndex = bitIndex;
                                    zmin.x = j;
                                    zmin.y = i;

                                    xIndex = j;
                                    for (m = 0; m <= k; m++)
                                    {
                                        yIndex = i + m - halfWindow;

                                        if (yIndex < 0 || yIndex >= height)
                                            continue;

                                        bitIndex = (yIndex * width) + xIndex;

                                        if (bitMixed1D[zmin.bitIndex] > bitMixed1D[bitIndex])
                                        {
                                            zmin.bitIndex = bitIndex;
                                            zmin.x = xIndex;
                                            zmin.y = yIndex;
                                        }
                                    }

                                    copyFromIndex = (zmin.y * width * 3) + (zmin.y * oldExtraBytes) + (zmin.x * 3);
                                    pixelIndex = (i * width * 3) + (i * newExtraBytes) + (j * 3);
                                    pNew[pixelIndex] = p[copyFromIndex];
                                    pNew[pixelIndex + 1] = p[copyFromIndex + 1];
                                    pNew[pixelIndex + 2] = p[copyFromIndex + 2];
                                }
                            }
                        }
                        break;
                    case FilterType.Max2D:
                        PHFastCpp.PHMax2D((int)bmpD.Scan0, (int)bmpNewD.Scan0, width, height, oldExtraBytes, newExtraBytes, k);
                        break;
                    case FilterType.Max1D:
                        PHFastCpp.PHMax1D((int)bmpD.Scan0, (int)bmpNewD.Scan0, width, height, oldExtraBytes, newExtraBytes, k);
                        break;
                    case FilterType.MidPoint:
                        PHFastCpp.PHMidPoint((int)bmpD.Scan0, (int)bmpNewD.Scan0, width, height, oldExtraBytes, newExtraBytes, k);
                        break;
                    case FilterType.Median:
                        PHFastCpp.PHMedian((int)bmpD.Scan0, (int)bmpNewD.Scan0, width, height, oldExtraBytes, newExtraBytes, k);
                        break;
                    case FilterType.AriMean:
                        PHFastCpp.PHArithmetic((int)bmpD.Scan0, (int)bmpNewD.Scan0, width, height, oldExtraBytes, newExtraBytes, k);
                        break;
                    case FilterType.GeoMean:
                        PHFastCpp.PHGeometric((int)bmpD.Scan0, (int)bmpNewD.Scan0, width, height, oldExtraBytes, newExtraBytes, k);
                        break;
                    case FilterType.AdaptiveMean:
                        PHFastCpp.PHAdaptiveMedian((int)bmpD.Scan0, (int)bmpNewD.Scan0, width, height, oldExtraBytes, newExtraBytes, k);
                        break;
                    default:
                        break;
                }

                bmp.UnlockBits(bmpD);
                bmpNew.UnlockBits(bmpNewD);
            }

            return bmpNew;
        }

        /// <summary>
        /// Masks the bit mixing.
        /// </summary>
        /// <param name="mask">The mask.</param>
        /// <returns> int array conting the values of bitmixing of the mask</returns>
        public int[,] MaskBitMixing(ImageStructure[,] mask)
        {
            int height = mask.GetLength(0);
            int width = mask.GetLength(1);
            int[,] bitMixedNo = new int[height, width];

            for (int i = 0; i < height; i++)
            {
                for (int j = 0; j < width; j++)
                {
                    bitMixedNo[i, j] = this.ColorBitMixing(mask[i, j]);
                }
            }

            return bitMixedNo;
        }

        /// <summary>
        /// Gets the random point.
        /// Colors the bit mixing.
        /// </summary>
        /// <param name="color">The color.</param>
        /// <returns> int numbet which represent the bitmixing value of the given color</returns>
        public int ColorBitMixing(ImageStructure color)
        {
            string newNumber = string.Empty;
            for (int i = 0; i < 8; i++)
            {
                newNumber += GetBit(color.B, i) - 48;
                newNumber += GetBit(color.G, i) - 48;
                newNumber += GetBit(color.R, i) - 48;
            }

            return ToDecimal(newNumber);
        }

        /// <summary>
        /// Arithmetics the mean.
        /// </summary>
        /// <param name="mask">The mask.</param>
        /// <returns>Arithmetic Mean value</returns>
        private static double ArithmeticMean(double[,] mask)
        {
            int height = mask.GetLength(0);
            int width = mask.GetLength(1);
            int size = height * width;
            double sum = 0;
            for (int i = 0; i < height; i++)
            {
                for (int j = 0; j < width; j++)
                {
                    sum += mask[i, j];
                }
            }

            return sum / size;
        }

        /// <summary>
        /// Geometrics the mean.
        /// </summary>
        /// <param name="mask">The mask.</param>
        /// <returns>
        /// Geometric Mean value
        /// </returns>
        private static double GeometricMean(double[,] mask)
        {
            int height = mask.GetLength(0);
            int width = mask.GetLength(1);
            int size = height * width;
            double product = 1;
            for (int i = 0; i < height; i++)
            {
                for (int j = 0; j < width; j++)
                {
                    product *= mask[i, j];
                }
            }

            return Math.Pow(product, 1.0 / size);
        }

        /// <summary>
        /// Facts the specified x.
        /// </summary>
        /// <param name="x">The x.</param>
        /// <returns>fact x</returns>
        private static double Fact(double x)
        {
            double fact = 1;
            double i = 1;
            while (i <= x)
            {
                fact = fact * i;
                i++;
            }

            return fact;
        }

        /// <summary>
        /// Contras the hharmonic mean.
        /// </summary>
        /// <param name="mask">The mask.</param>
        /// <param name="q">The q.</param>
        /// <returns>Contra Hharmonic Mean</returns>
        private static double ContraHharmonicMean(int[,] mask, double q)
        {
            double sum1 = 0, sum2 = 0;
            int height = mask.GetLength(0);
            int width = mask.GetLength(1);
            int size = height * width;

            for (int i = 0; i < height; i++)
            {
                for (int j = 0; j < width; j++)
                {
                    sum1 += Math.Pow(mask[i, j], q + 1);
                    sum2 += Math.Pow(mask[i, j], q);
                }
            }

            return sum1 / sum2;
        }

        /// <summary>
        /// Adaptives the median.
        /// </summary>
        /// <param name="biggestMask">The biggest mask.</param>
        /// <returns>Window size</returns>
        private static int AdaptiveMedian(int[,] biggestMask)
        {
            int[,] mask;
            int[] mask1D;
            int windowSize = 1;
            int k = biggestMask.GetLength(0);
            int halfK = (k / 2) + 1;
            int zxy = biggestMask[halfK - 1, halfK - 1];
            while (true)
            {
                mask = new int[windowSize, windowSize];
                for (int i = 0; i < windowSize; i++)
                {
                    for (int j = 0; j < windowSize; j++)
                    {
                        mask[i, j] = biggestMask[i + halfK - windowSize, j + halfK - windowSize];
                    }
                }

                mask1D = ReshapeIn1D(mask);
                int zmin = 0, zmax = 0, zmed = 0;

                // zmin = GetFililterValues(mask1D,FilterType.Min);
                // zmax = GetFililterValues(mask1D,FilterType.Max);
                // zmed = GetFililterValues(mask1D,FilterType.Median);
                GetMinMaxMed(mask1D, ref zmin, ref zmax, ref zmed);

                if (zmed - zmin > 0 && zmed - zmax < 0)
                {
                    if (zxy - zmin > 0 && zxy - zmax < 0)
                    {
                        return zxy;
                    }
                    else
                    {
                        return zmed;
                    }
                }
                else
                {
                    windowSize++;
                    if (windowSize > halfK)
                    {
                        return zxy;
                    }
                }
            }
        }

        /// <summary>
        /// Reshapes the in1 D.
        /// </summary>
        /// <param name="mask">The mask.</param>
        /// <returns>Reshaped 2D array to 1D</returns>
        private static int[] ReshapeIn1D(int[,] mask)
        {
            int k = mask.GetLength(0);
            int[] newMask = new int[k * k];
            int count = 0;
            for (int i = 0; i < k; i++)
            {
                for (int j = 0; j < k; j++)
                {
                    newMask[count++] = mask[i, j];
                }
            }

            return newMask;
        }

        public static Bitmap AddSalt_PepperFast(Bitmap img, float saltProbability, float pepperProbability, int noisePercentage)
        {
            int imgHeight = img.Height;
            int imgWidth = img.Width;

            unsafe
            {
                BitmapData bmd, bmdR;
                bmd = img.LockBits(new Rectangle(0, 0, imgWidth, imgHeight), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
                int extraBytes = bmd.Stride - imgWidth * 3;
                PHFastCpp.PHAddSalt_PepperNoise((int)bmd.Scan0, imgWidth, imgHeight, extraBytes, saltProbability, pepperProbability, noisePercentage);

                img.UnlockBits(bmd);

                return img;
            }
        }
    }
}